﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;

namespace Salient.Web
{
    ///<summary>
    /// <para>A group of static methods that build and execute HttpWebRequest.</para>
    /// <para>Useful for automating client requests for the testing of Http handlers.</para>
    ///</summary>
    // BUG: PostData processing uses .ToString() so dates are being truncated to the second. Need special case code
    // TODONE: handle GZIP and other compresses returns - SO API CASE IN POINT
    // TODO: really should obsolete my javascriptserializer extensions and use newtonsoft.json
    //       allow injection of serializer and/or options

    public static class RequestBuilder
    {

        // TODO: send gzip if supported
        // <summary>
        // Determines if GZip is supported
        // </summary>
        // <returns></returns>
        //public static bool IsGZipSupported()
        //{
        //    string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
        //    if (!string.IsNullOrEmpty(AcceptEncoding) &&
        //         (AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
        //        return true;
        //    return false;
        //}
        //public static void GZipEncodePage()
        //{
        //    if (IsGZipSupported())
        //    {
        //        HttpResponse Response = HttpContext.Current.Response;

        //        string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
        //        if (AcceptEncoding.Contains("gzip"))
        //        {
        //            Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
        //                                      System.IO.Compression.CompressionMode.Compress);
        //            Response.AppendHeader("Content-Encoding", "gzip");
        //        }
        //        else
        //        {
        //            Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
        //                                      System.IO.Compression.CompressionMode.Compress);
        //            Response.AppendHeader("Content-Encoding", "deflate");
        //        }
        //    }
        //}

        #region Http Request

        ///<summary>
        ///</summary>
        public static string UserAgent = string.Empty;

        ///<summary>
        ///</summary>
        public static DecompressionMethods AutomaticDecompression = DecompressionMethods.GZip;

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest.</para>
        /// <para>This is the base method for all Request and Response methods except PostFile.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="method">
        /// <para>The http method of the request.</para>
        /// <para>Currently supported options are <code>HttpMethod.Post</code> and <code>HttpMethod.Get</code></para>
        /// </param>
        /// <param name="contentType">
        /// <para>The content-type of the request.</para>
        /// <para>Currently supported options are <code>ContentType.None</code>, <code>ContentType.ApplicationForm</code>, <code>ContentType.ApplicationJson</code> and <code>ContentType.TextJson</code></para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        /// TODO: need a json query string encoding algo to support WebGet with complex objects
        public static HttpWebRequest Create(string requestUri, HttpMethod method, ContentType contentType, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            if (method == HttpMethod.Get && postData != null)
            {
                requestUri = AppendQueryString(requestUri, ToQueryString(postData));
            }

            HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUri);
            webRequest.UserAgent = UserAgent;
            webRequest.AutomaticDecompression = AutomaticDecompression;
            switch (AutomaticDecompression)
            {
                case DecompressionMethods.None:

                    break;
                case DecompressionMethods.GZip:
                    webRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip");
                    break;
                case DecompressionMethods.Deflate:
                    webRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "deflate");
                    break;
            }

            webRequest.Method = method.ToString().ToUpperInvariant();
            webRequest.ContentType = contentType.AsString();
            webRequest.CookieContainer = cookies;
            AppendRequestHeaders(headers, webRequest);

            if (postData != null && method == HttpMethod.Post)
            {
                byte[] data = new byte[] { };

                if (postData.GetType() == typeof(string))
                {
                    data = Encoding.UTF8.GetBytes((string)postData);
                }

                else
                {
                    switch (contentType)
                    {
                        case ContentType.ApplicationJson:
                        case ContentType.TextJson:
                            JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
                            string postDataAsJson = jsSerializer.Serialize(postData);
                            data = Encoding.UTF8.GetBytes(postDataAsJson);

                            break;
                        case ContentType.None:
                        case ContentType.ApplicationForm:
                            string postDataAsQuery = ToQueryString(postData);
                            data = Encoding.UTF8.GetBytes(postDataAsQuery);
                            break;
                    }

                }
                webRequest.ContentLength = data.Length;

                //TODO: investigate TransportContext
                using (Stream reqStream = webRequest.GetRequestStream())
                {
                    reqStream.Write(data, 0, data.Length);
                }
            }

            return webRequest;
        }

        #region Convenience Overloads of CreateRequest

        #region Get

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with empty content-type.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest Get(string requestUri)
        {
            return Get(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with empty content-type.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest Get(string requestUri, object postData)
        {
            return Get(requestUri, postData, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with empty content-type.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest Get(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.None, postData, cookies, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with empty content-type.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest Get(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.None, postData, cookies, headers);
        }

        #endregion

        #region Json Get

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'application/json; charset=UTF-8'.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonApp(string requestUri)
        {
            return GetJsonApp(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'application/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonApp(string requestUri, object postData)
        {
            return GetJsonApp(requestUri, postData, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'application/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonApp(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.ApplicationJson, postData, cookies, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'application/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonApp(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.ApplicationJson, postData, cookies, headers);
        }


        #endregion

        #region Json Text Get

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'text/json; charset=UTF-8'.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonText(string requestUri)
        {
            return GetJsonText(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'text/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonText(string requestUri, object postData)
        {
            return GetJsonText(requestUri, postData, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'text/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonText(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.TextJson, postData, cookies, null);
        }


        /// <summary>
        /// Builds and returns an HttpWebRequest GET with content-type 'text/json; charset=UTF-8'.
        /// <paramref name="postData"/> is serialized as a query string and appended to the Uri.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest GetJsonText(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Get, ContentType.TextJson, postData, cookies, headers);
        }


        #endregion

        #region Form Post


        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/x-www-form-urlencoded; charset=UTF-8".
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostForm(string requestUri)
        {
            return PostForm(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/x-www-form-urlencoded; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a query string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostForm(string requestUri, object postData)
        {
            return PostForm(requestUri, postData, null, null);
        }


        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/x-www-form-urlencoded; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a query string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostForm(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.ApplicationForm, postData, cookies, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/x-www-form-urlencoded; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a query string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostForm(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.ApplicationForm, postData, cookies, headers);
        }

        #endregion

        #region Json Post


        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/json; charset=UTF-8".
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonApp(string requestUri)
        {
            return PostJsonApp(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonApp(string requestUri, object postData)
        {
            return PostJsonApp(requestUri, postData, null, null);
        }


        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonApp(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.ApplicationJson, postData, cookies, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "application/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonApp(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.ApplicationJson, postData, cookies, headers);
        }

        #endregion

        #region Json Text Post

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "text/json; charset=UTF-8".
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonText(string requestUri)
        {
            return PostJsonText(requestUri, null, null, null);
        }

        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "text/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonText(string requestUri, object postData)
        {
            return PostJsonText(requestUri, postData, null, null);
        }


        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "text/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonText(string requestUri, object postData, CookieContainer cookies)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.TextJson, postData, cookies, null);
        }



        /// <summary>
        /// Builds and returns an HttpWebRequest POST with content-type "text/json; charset=UTF-8".
        /// <paramref name="postData"/> is serialized as a JSON string and posted as request body.
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">
        /// <para>Optional, A NameValueCollection or object. Anonymous types are acceptable.</para>
        /// <para>If value is a string and method is POST, the value is posted as is in the request body.</para>
        /// <para>The object will be serialized appropriately for the request method and content-type before being applied to the request. i.e.</para>
        /// <para>For form 'Post' requests, the postData object will be serialized into a Url encoded key-value string an streamed into the request body.</para>
        /// <para>For a JSON 'Post', it will be JSON serialized and streamed into the request body.</para>
        /// <para>For a 'Get' request it will be Url encoded into a query string and intelligently appended to the Uri. If the Uri is bare, the query string will be appended with a '?'. If the Uri already has a query string the new query will be appended with a '&amp;'.</para>
        /// <para>Note: any object that is shaped like the target method's arguments is acceptable.</para>
        /// <para>Conveniently this includes anonymous types. For a 'Get' or form 'Post' a NameValue collection may be appropriate as it is able to accept multiple values for a single key to fully simulate the possible shape of a form.</para>
        /// <para>When creating anonymous types as input parameters, you are not required to prototype the target type exactly. You MUST prototype non-nullable properties, including value types and structs, but you may omit any nullable properties, including Nullable&lt;T> and reference types, that you do not need to send.</para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(string input)</code> would be <code>var postData = new { input = "foo" };</code></para>
        /// <para>An anonymous type suitable as postData for the method <code>public Result PutSessionVar(Result input)</code> would be <code>var postData = new { input = new Result() };</code> or <code>var postData = new { input = new { Message = "message", Session = "session", Value = "value" } };</code>  or <code>var postData = new { input = new { Message = "message" } };</code></para>
        /// </param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostJsonText(string requestUri, object postData, CookieContainer cookies, NameValueCollection headers)
        {
            return Create(requestUri, HttpMethod.Post, ContentType.TextJson, postData, cookies, headers);
        }


        #endregion

        #endregion

        #endregion

        #region File Post

        // Reference: 
        // http://tools.ietf.org/html/rfc1867
        // http://tools.ietf.org/html/rfc2388
        // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2


        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="files">File to post</param>
        /// <param name="postData">Optional. A NameValueCollection containing form fields to post with file data</param>
        /// <param name="cookies">
        /// <para>Optional. Sharing a CookieCollection between requests is required to maintain session state, FormsAuthentication tickets and other cookies.</para>
        /// </param>
        /// <param name="headers">
        /// <para>Optional. Http headers to be added to the request.</para>
        /// </param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, UploadFile[] files, NameValueCollection postData, CookieContainer cookies, NameValueCollection headers)
        {
            if (requestUri == null)
            {
                throw new ArgumentNullException("requestUri");
            }

            if (files == null || files.Length == 0)
            {
                throw new ArgumentNullException("files", "No files were supplied. Use PostForm instead");
            }

            using (MemoryStream outputStream = new MemoryStream())
            {
                string boundary = "----------" + DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture);

                outputStream.AppendText(string.Format("--{0}\r\n", boundary), Encoding.UTF8);

                outputStream.AppendText(BuildFormPart(postData, boundary), Encoding.UTF8);


                for (int i = 0; i < files.Length; i++)
                {
                    var file = files[i];

                    const string fileHeaderFormat = "Content-Disposition: form-data; name=\"{0}\"; {1}\r\nContent-Type: {2}\r\n\r\n";

                    string fileHeader = string.Format(fileHeaderFormat, file.FieldName, string.IsNullOrEmpty(file.FileName)
                        ? string.Empty : string.Format(CultureInfo.InvariantCulture, "filename=\"{0}\";", Path.GetFileName(file.FileName)), file.ContentType);

                    outputStream.AppendText(fileHeader, Encoding.UTF8);

                    // buffer write the file data. we are going to end up with the whole file in memory
                    // anyway but no reason not to buffer the read

                    byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)file.Data.Length))];
                    int bytesRead;
                    while ((bytesRead = file.Data.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        outputStream.Write(buffer, 0, bytesRead);
                    }

                    if (i == files.Length - 1)
                    {
                        outputStream.AppendText(String.Format("\r\n--{0}--\r\n", boundary), Encoding.UTF8);

                    }
                    else
                    {
                        outputStream.AppendText(String.Format("\r\n--{0}\r\n", boundary), Encoding.UTF8);
                    }
                }


                HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(requestUri);
                AppendRequestHeaders(headers, webrequest);
                webrequest.Method = "POST";
                if (cookies != null)
                {
                    webrequest.CookieContainer = cookies;
                }
                webrequest.ContentType = "multipart/form-data; boundary=" + boundary;

                webrequest.ContentLength = outputStream.Length;
                //TODO: investigate TransportContext
                webrequest.GetRequestStream().Write(outputStream.ToArray(), 0, (int)outputStream.Length);

                return webrequest;
            }
        }

        #region PostFile Overloads

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="file">File to post</param>
        /// <param name="postData">Optional. A NameValueCollection containing form fields to post with file data</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, UploadFile file, NameValueCollection postData)
        {
            return PostFile(requestUri, new[] { file }, postData, null, null);
        }

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="file">File to post</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, UploadFile file)
        {
            return PostFile(requestUri, new[] { file }, null, null, null);
        }

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="fileName">The physical path of the file to upload</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, string fileName)
        {
            using (var file = new UploadFile(fileName))
            {
                return PostFile(requestUri, new[] { file }, null, null, null);
            }
        }

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="fileName">The physical path of the file to upload</param>
        /// <param name="data">An open, positioned stream containing the file data</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, string fileName, Stream data)
        {
            using (var file = new UploadFile(data, fileName))
            {
                return PostFile(requestUri, new[] { file }, null, null, null);
            }
        }


        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">Optional. A NameValueCollection containing form fields to post with file data</param>
        /// <param name="fileName">The physical path of the file to upload</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, NameValueCollection postData, string fileName)
        {
            using (var file = new UploadFile(fileName))
            {
                return PostFile(requestUri, new[] { file }, postData, null, null);
            }
        }

        /// <summary>
        /// <para>Builds and returns an HttpWebRequest that uploads a stream using a multipart/form-data POST.</para>
        /// <para>This is the base method for all Request and Response PostFile methods.</para>
        /// </summary>
        /// <param name="requestUri">
        /// <para>Absolute Uri of resource</para>
        /// </param>
        /// <param name="postData">Optional. A NameValueCollection containing form fields to post with file data</param>
        /// <param name="fileName">The physical path of the file to upload</param>
        /// <param name="data">An open, positioned stream containing the file data</param>
        /// <returns></returns>
        public static HttpWebRequest PostFile(string requestUri, NameValueCollection postData, string fileName, Stream data)
        {
            using (var file = new UploadFile(data, fileName))
            {
                return PostFile(requestUri, new[] { file }, postData, null, null);
            }
        }


        #endregion


        #endregion

        #region Misc utility methods

        private static string BuildFormPart(NameValueCollection postData, string boundary)
        {
            const string formPartFormat = "Content-Disposition: form-data; name=\"{0}\";\r\ncontent-type: text/plain;charset=UTF-8\r\n\r\n{1}\r\n";
            StringBuilder sbForm = new StringBuilder();
            if (postData != null)
            {
                foreach (string key in postData.AllKeys)
                {
                    string[] values = postData.GetValues(key);
                    if (values != null)
                        foreach (string value in values)
                        {

                            sbForm.AppendFormat(formPartFormat, key, value);
                            sbForm.AppendFormat("--{0}\r\n", boundary);
                        }
                }
            }


            string formPart = sbForm.ToString();
            return formPart;
        }


        private static void AppendRequestHeaders(NameValueCollection headers, WebRequest webrequest)
        {
            if (headers != null)
            {
                // set the headers
                foreach (string key in headers.AllKeys)
                {
                    string[] values = headers.GetValues(key);
                    if (values != null)
                        foreach (string value in values)
                        {
                            webrequest.Headers.Add(key, value);
                        }
                }
            }
        }


        /// <summary>
        /// TODO: move these methods into a static class as extensiont
        /// <para>Builds an UrlEncoded query string from a NameValueCollection or an object.</para>
        /// <para>Result is suitable for use in a Url or as post body.</para>
        /// </summary>
        /// <param name="postData">A NameValueCollection or object. Anonymous types are acceptable.</param>
        /// <returns></returns>
        public static string ToQueryString(object postData)
        {

            string encoded;
            NameValueCollection nvc = postData as NameValueCollection;
            if (nvc != null)
            {
                encoded = string.Join("&",
                                      Array.ConvertAll(nvc.AllKeys,
                                                       key =>
                                                       string.Format(CultureInfo.InvariantCulture, "{0}={1}",
                                                                     HttpUtility.UrlEncode(key),
                                                                     HttpUtility.UrlEncode(nvc[key]))));
            }
            else
            {
                // try to create a query string from any object that can be viewed as a Key:Value container.

                JavaScriptSerializer jsSerializer = new JavaScriptSerializer();

                string json = jsSerializer.Serialize(postData);

                Dictionary<string, object> jsob =
                    (Dictionary<string, object>)jsSerializer.DeserializeObject(json);

                List<string> items = new List<string>();

                foreach (KeyValuePair<string, object> q in jsob)
                {
                    items.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", HttpUtility.UrlEncode(q.Key),
                                            HttpUtility.UrlEncode(q.Value.ToString())));
                }
                encoded = string.Join("&", items.ToArray());
            }

            return encoded;
        }

        /// <summary>
        /// Appends a query string to an Uri using the appropriate operator.
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public static string AppendQueryString(string uri, string query)
        {
            if (string.IsNullOrEmpty(query))
            {
                return uri;
            }
            var lUri = new Uri(uri);

            return new Uri(uri + (string.IsNullOrEmpty(lUri.Query) ? "?" : "&") + query).ToString();
        }


        #endregion


        #region Stream Extensions

        ///<summary>
        ///</summary>
        ///<param name="stream"></param>
        ///<param name="value"></param>
        ///<param name="encoding"></param>
        public static void AppendText(this Stream stream, string value, Encoding encoding)
        {
            var bytes = encoding.GetBytes(value);
            stream.Write(bytes, 0, bytes.Length);
        }

        /// <summary>
        /// Reads stream into a byte array, which is returned.
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static byte[] Bytes(this Stream stream)
        {
            byte[] buffer = new byte[32768];
            using (MemoryStream ms = new MemoryStream())
            {
                while (true)
                {
                    int read = stream.Read(buffer, 0, buffer.Length);
                    if (read <= 0)
                    {
                        return ms.ToArray();
                    }
                    ms.Write(buffer, 0, read);
                }
            }
        }

        /// <summary>
        /// Reads stream into a string, which is returned.
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static string Text(this Stream stream)
        {
            StreamReader reader = new StreamReader(stream, Encoding.UTF8);
            return reader.ReadToEnd();
        }

        #endregion


        /// <summary>
        /// TODO: Test this 
        /// Constructs a QueryString (string).
        /// Consider this method to be the opposite of "System.Web.HttpUtility.ParseQueryString"
        /// </summary>
        /// <param name="parameters">NameValueCollection</param>
        /// <returns>String</returns>
        public static String ToQueryString(this NameValueCollection parameters)
        {
            List<String> items = new List<String>();

            foreach (String name in parameters)
            {
                string[] values = parameters.GetValues(name);
                if (values != null)
                {
                    items.Add(String.Concat(name, "=",
                                            string.Join(",", values
                                                                 .Select(v => HttpUtility.UrlEncode(v)).ToArray())));
                }
            }

            return String.Join("&", items.ToArray());
        }

    }
}
